import random
import re
from datetime import datetime

from flask import abort, jsonify
from flask import current_app
from flask import make_response
from flask import request
from flask import session

from info import constants
from info import redis_store, db
from info.models import User
from info.utils.captcha.captcha import captcha
from info.utils.response_code import RET
from . import passport_blue


# 开发中,后端人员来定义路由地址/请求方式/参数/返回值等

# 用户退出
@passport_blue.route('/logout',methods = ["POST"])
def logout():
    """ 清楚session中的对应登录之后保存的信息
    """
    session.pop("user_id",None)
    session.pop("nick_name",None)
    # 清楚管理员

    session.pop('is_admin', False)
    # 返回结果
    return jsonify(errno = RET.OK,errmsg = "ok")

# 用户登录
@passport_blue.route('/login',methods = ["POST"])
def login():
    #　一．获取参数
    mobile = request.json.get("mobile")
    password = request.json.get("password")

    # 二,校验参数
    # 2.1 完整性
    if not all([mobile,password]):
        return jsonify(errno =RET.PARAMERR,errmsg = "参数不全")

    # 2.2 手机号
    if not re.match(r'^1[3-9][0-9]{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号错误")
    # 三,逻辑处理
    # 1.查询用户
    try:
        user = User.query.filter_by(mobile = mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据库错误")
    #  2. 验证密码或用户是否存在
    if not user or not user.check_passowrd(password):
    # if not user or user.password_hash != password:
        return jsonify(errno=RET.DATAERR, errmsg="手机号或密码错误")
    # 3.设置登录-->设置session
    session["user_id"] = user.id
    session["nick_name"] = user.nick_name

    # 4.更新最后登录时间
    user.last_login = datetime.now()
    try:
        # 跟新时间以后要进行提交
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据库错误")
    # 四 返回数据
    return jsonify(errno=RET.OK, errmsg="登录成功")


# 注册用户
@passport_blue.route('/register',methods = ["POST"])
def register():
    # 一,获取参数
    mobile = request.json.get("mobile")
    sms_code = request.json.get("sms_code")
    password = request.json.get("password")
    # 二,校验参数
    # 2.1 完整性
    if not all([mobile,password,sms_code]):
        return jsonify(errno = RET.PARAMERR,errmsg = "参数不全")
    # 2.2 手机号
    if not re.match(r"^1[3-9][0-9]{9}$",mobile):
        return jsonify(errno = RET.PARAMERR,errmsg = "手机号码错误")
    # 三.逻辑处理
    # 1.对比验证码
    # 1.1从redis获取数据
    try: # sms_code 是短信验证码
        real_sms_code = redis_store.get("sms_code_"+mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno= RET.DBERR,errmsg = "数据库错误")
    #  如果没有验证码,或者验证码有问题
    if not real_sms_code:
        return jsonify(errno = RET.NODATA,errmsg = "短信验证码已过期,或者手机号填写错误")
    # 1.2 对比短信验证码
    if real_sms_code != sms_code:
        return jsonify(errno = RET.DATAERR,errmsg= "验证码填写错误")

    # 1.3 删除短信验证码
    try:
        redis_store.delete("sms_code_"+mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno = RET.DBERR,errmsg = "删除redis数据库错误")
    # 2.用户注册
    # 2.1判断是否注册
    try:
        user = User.query.filter_by(mobile = mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno = RET.DBERR,errmsg = "查询mysql数据库错误")

    if user :
        return jsonify(errno = RET.DATAEXIST,errmsg = "手机号已注册")
    # 2,2 创建用户模型
    user = User()
    user.nick_name = mobile # 没有昵称,先用手机号替代
    user.mobile = mobile
    # TODO (zhubo) 未做密码加密处理
    user.password = password
    # 2.3 提交数据库
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        # 数据库回滚
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="数据库错误")
    # 3.设置登录
    # 存储是:1.常用信息如昵称或手机号 2.用户id(用户可能存在一定的变化)
    # 场景: 用户先注册,1小时后发起修改用户名请求,但是者中间该用户以备后台删除
    session["user_id"] = user.id
    session["nick_name"] = user.nick_name
    # 四返回数据

    return jsonify(errno = RET.OK,errmsg = "注册成功")

# URL:/sms_code
# 请求方式:POST
# 参数:image_code_id,mibile,image_code
# 返回数据:json数据

@passport_blue.route('/sms_code',methods = ["POST"])
def sms_code():
    """
    1.获取参数
    2.校验参数
    3.从redis获取图像验证码
    4.对比验证码
    5.生成短信验证码
    6.保存验证码到redis
    7.发送短信
    8.返回成功信息
    """
    # 一,获取参数
    # 1,获取参数
    # data = request.data
    # json_data = json.loads(data)
    # 开发中获取JSON数据,会使用request.json,直接可以获取字典数据,方便解析数据
    json_data = request.json
    image_code_id = json_data.get("image_code_id")
    mobile = json_data.get("mobile")
    image_code = json_data.get("image_code")

    # 二.校验参数
    # 2.校验参数
    # 2.1 完整性  如果不完整
    if not all([image_code_id,image_code,mobile]):
        # 如果数据有任何一个缺失,都会进入这里
        # 返回错误时,一般会返回字典,至少包含错误码和错误信息
        #{"error":"100","errmsg":"参数不全"}
        # 前后端通常以JSOn数据进行数据沟通

        return jsonify(errno = RET.PARAMERR,errmsg = "参数不全")
    # 2.2 手机号  如果没有手机号
    if not re.match(r"^1[3-9][0-9]{9}$",mobile):
        return jsonify(errno = RET.PARAMERR,errmsg = "手机号码错误")
    # 三,逻辑处理
    '''
    3.从redis获取图像验证码
    4.对比验证码
    5.生成短信验证码
    6.保存验证码到redis
    7发送短信

    '''
    # 3.从redis获取图像验证码
    try: # 如果不能正常获取抛出一个异常
        real_image_code = redis_store.get("image_code_id_"+ image_code_id)
    except Exception as e:
        #  记录日志异常信息
        current_app.logger.error(e)
        # 返回数据库错误信息
        return jsonify(errno = RET.DBERR,errmsg = "访问redis数据库错误")
    # 如果没有验证码,获取获取不到验证码,反正就是不能正常获取验证码就会返回异常提示
    if not real_image_code:
        return jsonify(errno = RET.NODATA,errmsg = "验证码已过期")
    #### 加入一个删除图片验证码
    # 图片验证码只有1次有效期,无论是否验证通过
    try:
        redis_store.delete("image_code_id_"+image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        # 为了用户体验,可以不用返回JSON 错误信息
        return jsonify(errno = RET.DBERR,errmsg = "删除redis数据库")

    # 4.对比验证码
    # ABCD:abcd 验证码忽略大小写 xxx.lower()
    if real_image_code.lower() != image_code.lower():
        return jsonify(errno = RET.DATAERR,errmsg= "验证码填写错误")
    ####　发送短信 验证短信的时候判断用户是否进行注册了

    ####  判断用户是否注册过
    try:
        user = User.query.filter_by(mobile = mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno = RET.DBERR,errmsg = "查询mysql数据库错误")

    if user:
        return jsonify(errno=RET.DATAEXIST, errmsg="手机号已注册")

    # 5.生成短信验证码
    # "123456"
    # "%06d": 生成6为数字,不足6位以0补齐
    sms_code_str = "%06d" % random.randint(0,999999)
    # 打印出验证码信息,实际上是日志信息
    current_app.logger.info(sms_code_str)

    # 6.保存验证码到redis
    try: # 保存失败是记录信息返回异常代码
        redis_store.setex("sms_code_" + mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code_str)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno = RET.DBERR,errmsg = "保存redis服务器失败")

    # 7.发送短信      mobile前端传过来的手机号码  sms_code_str 随机生成的6位数字,5 : 分钟过期,1,模板1
    # # result : 发送成功返回一个000000
    # result = CCP().send_template_sms(mobile,[sms_code_str,5],1)
    # if result != "000000":
    #     return jsonify(errno= RET.THIRDERR,errmsg = "发送短信失败")

    # 四返回数据
    return jsonify(errno = RET.OK,errmsg = "发送短信成功")

# 开发中,后端人员来定义路由地址/请求方式/参数/返回值等
# URL:/image_code
# 请求方式:GET(一般来说,获取数据用GET,提交数据用POST)
# 参数: image_code_id
# 返回数据:Json数据(模板/JSON数据)

@passport_blue.route('/image_code')
def get_image_code():
    '''
    1. 获取UUID
    2. 生成图像验证码
    3. 保存数据库
    4. 返回图像
    '''

    # 1.获取uuid 从浏览器生成
    image_code_id = request.args.get("image_code_id")
    #  如果image_code_id 为空抛出错误
    if not image_code_id:
        # 403错误表示资源不可用
        return abort(403)


    # 2.生成图像验证码 text 是字母 image_data是图片
    name,text,image_data = captcha.generate_captcha()
    # 3.保存图像到数据库         字符串拼接            时间,单位是秒 ,最后的是图片
    # 防止保存服务器失败
    try:
        # 增加类型注释 redis_store = None # type: redis.StrictRedis
        #redis_store.setex("image_code_id_"+ image_code_id,300,text)
        redis_store.setex("image_code_id_" + image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
    except Exception as e:
        # 如果保存不到数据库记录日志,抛出一个日常
        # logging.error(e)
        current_app.logger.error(e)
        # 返回错误
        return abort(500)
    # 返回图像  更改图片显示类型
    response = make_response(image_data)
    response.headers["Content-Type"] = "image/jpg"
    return response
